<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <script>
      // 1.闭包的概念：
      // 闭包:闭包是指有权访问另一个函数作用域中的变量的函数。
      // 创建闭包的常见方式，就是在一个函数内部创建另一个函数
      //闭包可以通过debug断点查看到。
      // 下述中的闭包函数指的就是fn函数。xxxxxx
    //   function fn() {
    //     var num = 10; //被访问的局部变量的函数 就是闭包
    //     function fnn() {
    //       console.log(num);
    //     }
    //     fnn();
    //   }
    //   fn();

      function fn() {
        // 由于fn是闭包函数，所以num这个变量不会在调用了fn()后立即销毁，因为还有别的函数会使用这个变量。
        // 只有等所以函数都调用完后才会销毁这个变量。
        var num = 10;
        return function () {
          return num;
        };
      }
      var f = fn();
      var a = f() + 5;
      console.log(a); //闭包的主要作用是 延伸了变量的作用范围 数据的私密性
      /*调用的是return function() {
                        console.log(num);
                    };这个函数 */

      /*
          闭包的形成
          1. 当做函数返回
            function fn() {
              const num = 10;
              return function() {
                  return num
              }
          }
          const f = fn()
          f();
          2. 函数当作参数传递

          function fn(callback) {
              const n = 10;
              console.log(callback());
          }
          const n = 50;
          fn(function() {
              return n
          })
          
          3.匿名自执行函数
            (function(index) {
              console.log(index);
          })(10)
          */

      // 闭包的作用之隐藏变量

      function getVal() {
        const data = {};
        return {
          set: function (key, val) {
            data[key] = val;
          },
          get: function (key) {
            return data[key];
          },
        };
      }
      const jsson = getVal();
      jsson.set("age", 18);
      console.log(jsson.get("age"), "年龄隐藏变量");

      // 使用闭包会重新创建作用域
      // 做闭包的问题时，我们还需要注意以下两点：
      //1、外层函数被多次调用，都会创建新的作用域，也就是说内层函数操作的外层函数的局部变量之间是不会影响的
      //         这句话简单来说就是我第一次调用这个外层函数
      // ，       使用了内部的局部变量 p；我第二次第三次再调用这个外层函数使用变量 p的时候，会创建新的作用域，
      //         并不会对之前的调用产生什么影响
      // 　　2、外层函数返回的内层函数被调用几次，内层函数操作的外层函数的局部变量就会变化几次
      function p() {
        let sum = 0;
        return function () {
          return sum++;
        };
      }
      //第一次调用
      const s = p();
      console.log(s(), s(), "第一次调用");

      //第二次调用
      const ss = p();
      console.log(ss(), ss(), ss(), "第二次调用");

      //第3次调用
      const sss = p();
      console.log(sss(), sss(), sss(), sss(), "第3次调用");

      // 闭包的坏处
      //闭包使函数内部的变量不能被内存释放， 这些变量就会占用内存， 内存消耗大， 可能会导致内存泄露
      //解决这个问题的办法就是在不使用这些变量时， 及时把不需要的局部变量全部删除
      // // 内存泄漏:对于那些我们不再使用的对象，但对于gc(垃圾回收)来说，他不知道我们是否还会调用，所有会保留
      // s = null; //不再使用
      // sss = null;
      // ss = null;
    </script>
  </body>
</html>
